/*
 * This file implements the data model service.
 * @author Group 10
 */
package securityfacade;

import java.io.IOException;
import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Map;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import rmiinterfaces.DataModelInterface;

import rmiinterfaces.RequestTypes.RequestType;
import rmiinterfaces.SecurityInterface;
import rmiinterfaces.SecurityInterface.AuthReturn;

// This class creates an RMI server that implements authentication. 
// It receives requests from GUI forms that are placed outside the LAN,
// forwards the result to the data model, and then sends the return value back to the form.
public class SecurityFacade extends java.rmi.server.UnicastRemoteObject
        implements SecurityInterface {

    int thisPort; //port to access the datamodel rmi server
    String thisAddress; //address of the datamodel rmi server
    Registry registry;    // rmi registry for lookup the remote objects.
    AuthenticationInterface authentication; //authentication implementation reference
    DataModelInterface dataModel; //reference to data model
    
    final static Logger logger = Logger.getLogger("Security Facade Logger");

    // Constructor for Security Facade.
    public SecurityFacade(String dmLocation) throws RemoteException {
        //set up logger
        try {
            // Security facade log file handling
            SimpleFormatter sf = new SimpleFormatter();
            FileHandler fh = new FileHandler("security_facade_log.txt", true);
            fh.setFormatter(sf);
            logger.addHandler(fh);
        } catch (IOException e) { //file handling exception
            System.out.println("Unable to start logging; proceeding without logs.");
        }
        logger.info("Security Facade process started");
        
        // establish RMI server for self
        try {
            // get the address of this host.
            thisAddress = (InetAddress.getLocalHost()).toString();

        } catch (Exception e) {
            throw new RemoteException("Can't get inet address.");
        }

        thisPort = 8814; 
        System.out.println("this address=" + thisAddress + ",port=" + thisPort);

        try {
            // create the registry and bind the name and object.
            registry = LocateRegistry.createRegistry(thisPort);
            registry.rebind("securityFacade", this);
        } catch (RemoteException e) {
            throw e;
        }

        // connect to data model
        String serverAddress = dmLocation;
        String serverPort = "1488";
        System.out.println("Establishing connection to DataModel at "
                + serverAddress + ":" + serverPort);
        try {
            // connect to registry and lookup the datamodel object
            registry = LocateRegistry.getRegistry(
                    serverAddress,
                    (new Integer(serverPort)).intValue());

            Remote obj = (registry.lookup("dataModel"));

            dataModel = (DataModelInterface) obj;
        } //handle exceptions generated during remote lookup
        catch (RemoteException e) {
            e.printStackTrace();
        } catch (NotBoundException e) {
            e.printStackTrace();
        }
    }

    // The main function for Security Facade
    public static void main(String[] args) {
        try {
            SecurityFacade sf = new SecurityFacade(args[0]);
            sf.setAuthentication(new AuthenticationImplementation());
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    // a setter for auth mechanism
    public void setAuthentication(AuthenticationInterface authenticationMechanism) {
        this.authentication = authenticationMechanism;
    }

    // a transparent implementation for request execution
    public Map<String, Object> execRequest(RequestType reqNum, Map<String, Object> params, String login, String psw) throws RemoteException {
        if (authentication.authenticate(login, psw).equals(AuthReturn.TRUE)) {
            //logger.log(Level.INFO, "Forwarding request" + reqNum.toString() + " by user {0} to Data Model", login);
            try {
                return dataModel.execRequest(reqNum, params);
            } catch (RemoteException re) {
                return null;
            }
        } else {
            // If this branch reached, someone may be trying to hack us!
            logger.log(Level.INFO, "Unsuccessful attempt to bypass authentication "
                    + "by user {0}", login);
            return null;
        }
    }

    // Implementation for logIn function from Security Interface
    public AuthReturn logIn(String login, String psw) throws RemoteException {
        AuthReturn answer = authentication.authenticate(login, psw);
        //Log login attempt to the user action log file
        if (answer.equals(AuthReturn.TRUE)) {
            logger.log(Level.INFO, "Successful login by user {0}", login);
        } else {
            logger.log(Level.INFO, "Unsuccessful login attempt by user {0}", login);
        }
        //return authentication return status
        return answer;
    }

    // Implementation for logOut function from Security Interface
    public AuthReturn logOut(String login, String psw) throws RemoteException {
        AuthReturn answer = authentication.authenticate(login, psw);
        //Log logout attempt to the user action log file
        if (answer.equals(AuthReturn.TRUE)) {
            logger.log(Level.INFO, "Successful logout by user {0}", login);
        } else {
            logger.log(Level.INFO, "Unsuccessful logout attempt by user {0}", login);
        }
        //return authentication return status
        return answer;
    }
}
